#include "vsf.h"
#if defined(VSFVM_VM) || defined(VSFVM_COMPILER)

#include "vsfvm.h"
#include "vsfvm_ext_std.h"

#ifdef VSFVM_VM
static enum vsfvm_ret_t vsfvm_ext_print(struct vsfvm_thread_t *thread)
{
	struct vsf_buffer_t buffer;
	struct vsfvm_var_t *var;

	for (uint8_t i = 0; i < thread->func.argc; i++)
	{
		var = vsfvm_get_func_argu_ref(thread, i);
		if (!var) return VSFVM_RET_ERROR;

		switch (var->type)
		{
		case VSFVM_VAR_TYPE_VALUE:
			vsfdbg_printf("%d", var->value);
			break;
		case VSFVM_VAR_TYPE_RESOURCES:
			if (vsfvm_get_res(thread->script, var->value, &buffer))
				return VSFVM_RET_ERROR;
			vsfdbg_printf("%s", buffer.buffer);
			break;
		case VSFVM_VAR_TYPE_FUNCTION:
			vsfdbg_printf("function@%d", var->uval16);
			break;
		case VSFVM_VAR_TYPE_INSTANCE:
			if (var->inst != NULL)
			{
				if (var->inst->c->op.print != NULL)
					var->inst->c->op.print(var);
				else
					vsfdbg_printb(var->inst->buffer.buffer,
						var->inst->buffer.size, 1, 0, false, true);
			}
			else
			{
				vsfdbg_prints("NULL");
			}
			break;
		default:
			vsfdbg_printf("unknown type(%d)", var->type);
		}
	}
	return VSFVM_RET_FINISHED;
}

static enum vsfvm_ret_t vsfvm_ext_memset(struct vsfvm_thread_t *thread)
{
	struct vsfvm_var_t *thiz = vsfvm_get_func_argu_ref(thread, 0);
	struct vsfvm_var_t *value = vsfvm_get_func_argu_ref(thread, 1);
	struct vsfvm_var_t *size = vsfvm_get_func_argu_ref(thread, 2);

	if (!thiz || !thiz->inst || (thiz->type != VSFVM_VAR_TYPE_INSTANCE))
		return VSFVM_RET_ERROR;

	memset(thiz->inst->buffer.buffer, value->uval8, size->uval32);
	return VSFVM_RET_FINISHED;
}

static enum vsfvm_ret_t vsfvm_ext_memcpy(struct vsfvm_thread_t *thread)
{
	struct vsfvm_var_t *dst = vsfvm_get_func_argu_ref(thread, 0);
	struct vsfvm_var_t *src = vsfvm_get_func_argu_ref(thread, 1);
	struct vsfvm_var_t *size = vsfvm_get_func_argu_ref(thread, 2);

	if (!src || !src->inst || !dst || !dst->inst ||
		(src->type != VSFVM_VAR_TYPE_INSTANCE) ||
		(dst->type != VSFVM_VAR_TYPE_INSTANCE))
	{
		return VSFVM_RET_ERROR;
	}

	memcpy(dst->inst->buffer.buffer, src->inst->buffer.buffer, size->uval32);
	return VSFVM_RET_FINISHED;
}

struct vsfvm_ext_array_t
{
	uint16_t dimension;
	uint16_t ele_size;
	uint32_t *dim_size;
	union
	{
		void *buffer;
		uint8_t *buf8;
		uint16_t *buf16;
		uint32_t *buf32;
	};
};

static enum vsfvm_ret_t vsfvm_ext_array_create(struct vsfvm_thread_t *thread)
{
	struct vsfvm_var_t *result = vsfvm_get_func_argu(thread, 0);
	struct vsfvm_var_t *dimension = vsfvm_get_func_argu_ref(thread, 0);
	struct vsfvm_var_t *ele_size = vsfvm_get_func_argu_ref(thread, 1);
	struct vsfvm_var_t *var;
	struct vsfvm_ext_array_t *arr;
	uint32_t size, buffsize, dim;

	if (!dimension || (thread->func.argc != (2 + dimension->uval8)) ||
		((ele_size->uval8 != 1) && (ele_size->uval8 != 2) && (ele_size->uval8 != 4)))
	{
		return VSFVM_RET_ERROR;
	}
	dim = dimension->uval8;

	size = sizeof(*arr) + dim * sizeof(uint32_t);
	buffsize = ele_size->uval8;
	for (uint8_t i = 0; i < dim; i++)
	{
		var = vsfvm_get_func_argu_ref(thread, i + 2);
		if (!var) return VSFVM_RET_ERROR;
		buffsize *= var->uval8;
	}
	size += buffsize;

	if (vsfvm_instance_alloc(result, size, &vsfvm_ext_array))
		return VSFVM_RET_ERROR;
	arr = (struct vsfvm_ext_array_t *)result->inst->buffer.buffer;
	arr->dimension = dim;
	arr->ele_size = ele_size->uval8;
	arr->dim_size = (uint32_t *)&arr[1];
	arr->buffer = &arr->dim_size[arr->dimension];
	for (uint8_t i = 0; i < dim; i++)
	{
		var = vsfvm_get_func_argu_ref(thread, i + 2);
		arr->dim_size[i] = var->uval8;
	}
	return VSFVM_RET_FINISHED;
}

static enum vsfvm_ret_t vsfvm_ext_array_get(struct vsfvm_thread_t *thread)
{
	struct vsfvm_var_t *result = vsfvm_get_func_argu(thread, 0);
	struct vsfvm_var_t *thiz = vsfvm_get_func_argu_ref(thread, 0);
	struct vsfvm_var_t *var;
	struct vsfvm_ext_array_t *arr;
	uint32_t pos, size, allsize;

	if (!thiz || !vsfvm_instance_of(thiz, &vsfvm_ext_array)) return VSFVM_RET_ERROR;
	arr = (struct vsfvm_ext_array_t *)thiz->inst->buffer.buffer;
	if (thread->func.argc != (1 + arr->dimension)) return VSFVM_RET_ERROR;

	pos = 0;
	allsize = 1;
	for (uint8_t i = 0; i < arr->dimension; i++)
	{
		var = vsfvm_get_func_argu_ref(thread, i + 1);
		size = 1;
		for (uint8_t j = i + 1; j < arr->dimension; j++)
			size *= arr->dim_size[j];
		pos += size * var->uval8;
		allsize *= arr->dim_size[i];
	}
	if (pos >= allsize) return VSFVM_RET_ERROR;

	vsfvm_instance_deref(thread, thiz);
	result->uval32 = 0;
	switch (arr->ele_size)
	{
	case 1: result->uval8 = arr->buf8[pos]; break;
	case 2: result->uval16 = arr->buf16[pos]; break;
	case 4: result->uval32 = arr->buf32[pos]; break;
	}
	result->type = VSFVM_VAR_TYPE_VALUE;
	return VSFVM_RET_FINISHED;
}

static enum vsfvm_ret_t vsfvm_ext_array_set(struct vsfvm_thread_t *thread)
{
	struct vsfvm_var_t *thiz = vsfvm_get_func_argu_ref(thread, 0);
	struct vsfvm_var_t *var;
	struct vsfvm_ext_array_t *arr;
	uint32_t pos, size, allsize;

	if (!thiz || !vsfvm_instance_of(thiz, &vsfvm_ext_array)) return VSFVM_RET_ERROR;
	arr = (struct vsfvm_ext_array_t *)thiz->inst->buffer.buffer;
	if (thread->func.argc < (1 + arr->dimension)) return VSFVM_RET_ERROR;

	pos = 0;
	allsize = 1;
	for (uint8_t i = 0; i < arr->dimension; i++)
	{
		var = vsfvm_get_func_argu_ref(thread, i + 1);
		size = 1;
		for (uint8_t j = i + 1; j < arr->dimension; j++)
			size *= arr->dim_size[j];
		pos += size * var->uval8;
		allsize *= arr->dim_size[i];
	}
	for (uint8_t i = 1 + arr->dimension; i < thread->func.argc; i++)
	{
		var = vsfvm_get_func_argu_ref(thread, i);
		if (pos >= allsize) return VSFVM_RET_ERROR;
		switch (arr->ele_size)
		{
		case 1: arr->buf8[pos++] = var->uval8; break;
		case 2: arr->buf16[pos++] = var->uval16; break;
		case 4: arr->buf32[pos++] = var->uval32; break;
		}
	}
	return VSFVM_RET_FINISHED;
}

static void vsfvm_ext_array_print(struct vsfvm_var_t *var)
{
	
}

static enum vsfvm_ret_t vsfvm_ext_buffer_create(struct vsfvm_thread_t *thread)
{
	struct vsfvm_var_t *result = vsfvm_get_func_argu(thread, 0);
	struct vsfvm_var_t *arg0 = vsfvm_get_func_argu_ref(thread, 0);
	uint8_t *buffer;

	if (!arg0) return VSFVM_RET_INVALID_PARAM;
	if (arg0->type == VSFVM_VAR_TYPE_INSTANCE)
	{
		if (thread->func.argc != 2)
			return VSFVM_RET_INVALID_PARAM;
		else
		{
			struct vsfvm_var_t *arg1 = vsfvm_get_func_argu_ref(thread, 1);
			uint8_t *ptr = arg0->inst->buffer.buffer;

			vsfvm_instance_deref(thread, result);
			if (vsfvm_instance_alloc(result, 0, &vsfvm_ext_buffer))
				return VSFVM_RET_ERROR;
			result->inst->buffer.buffer = ptr;
			result->inst->buffer.size = arg1->uval32;
		}
	}
	else
	{
		if (thread->func.argc != 1)
			return VSFVM_RET_INVALID_PARAM;
		else
		{
			uint32_t size = arg0->uval32;
			if (vsfvm_instance_alloc(result, arg0->uval32, &vsfvm_ext_buffer))
				return VSFVM_RET_ERROR;
			buffer = result->inst->buffer.buffer;
			memset(buffer, 0, size);
		}
	}
	return VSFVM_RET_FINISHED;
}

static enum vsfvm_ret_t vsfvm_ext_buffer_get_size(struct vsfvm_thread_t *thread)
{
	struct vsfvm_var_t *result = vsfvm_get_func_argu(thread, 0);
	struct vsfvm_var_t *thiz = vsfvm_get_func_argu_ref(thread, 0);
	uint32_t size;

	if (!thiz || !vsfvm_instance_of(thiz, &vsfvm_ext_buffer))
		return VSFVM_RET_INVALID_PARAM;

	size = thiz->inst->buffer.size;
	vsfvm_instance_deref(thread, thiz);
	result->uval32 = size;
	result->type = VSFVM_VAR_TYPE_VALUE;
	return VSFVM_RET_FINISHED;
}

static enum vsfvm_ret_t vsfvm_ext_pointer_create(struct vsfvm_thread_t *thread)
{
	struct vsfvm_var_t *result = vsfvm_get_func_argu(thread, 0);
	struct vsfvm_var_t *arg0 = vsfvm_get_func_argu_ref(thread, 0);

	if (!arg0) return VSFVM_RET_INVALID_PARAM;
	if (arg0->type == VSFVM_VAR_TYPE_INSTANCE)
	{
		if (thread->func.argc != 3)
			return VSFVM_RET_INVALID_PARAM;
		else
		{
			struct vsfvm_var_t *offset = vsfvm_get_func_argu_ref(thread, 1);
			struct vsfvm_var_t *size = vsfvm_get_func_argu_ref(thread, 2);
			uint8_t *addr = arg0->inst->buffer.buffer;

			vsfvm_instance_deref(thread, result);
			if ((size->uval32 != 1) || (size->uval32 != 2) || (size->uval32 != 4))
				return VSFVM_RET_INVALID_PARAM;
			if (vsfvm_instance_alloc(result, 0, &vsfvm_ext_pointer))
				return VSFVM_RET_ERROR;
			result->inst->buffer.buffer = &addr[offset->uval32 * size->uval32];
			result->inst->buffer.size = size->uval32;
		}
	}
	else
	{
		if (thread->func.argc != 2)
			return VSFVM_RET_INVALID_PARAM;
		else
		{
			struct vsfvm_var_t *size = vsfvm_get_func_argu_ref(thread, 1);
			uint8_t *addr = (uint8_t *)arg0->uval32;

			if ((size->uval32 != 1) && (size->uval32 != 2) && (size->uval32 != 4))
				return VSFVM_RET_INVALID_PARAM;
			if (vsfvm_instance_alloc(result, 0, &vsfvm_ext_pointer))
				return VSFVM_RET_ERROR;
			result->inst->buffer.buffer = addr;
			result->inst->buffer.size = size->uval32;
		}
	}
	return VSFVM_RET_FINISHED;
}

static enum vsfvm_ret_t vsfvm_ext_pointer_set(struct vsfvm_thread_t *thread)
{
	struct vsfvm_var_t *thiz = vsfvm_get_func_argu_ref(thread, 0);
	struct vsfvm_var_t *offset = vsfvm_get_func_argu_ref(thread, 1);
	struct vsfvm_var_t *value = vsfvm_get_func_argu_ref(thread, 2);
	struct vsf_buffer_t buffer;

	if (!thiz || !vsfvm_instance_of(thiz, &vsfvm_ext_pointer))
		return VSFVM_RET_INVALID_PARAM;
	buffer = thiz->inst->buffer;
	switch (buffer.size)
	{
	case 1:
		((uint8_t *)buffer.buffer)[offset->uval32] = value->uval8;
		break;
	case 2:
		((uint16_t *)buffer.buffer)[offset->uval32] = value->uval16;
		break;
	case 4:
		((uint32_t *)buffer.buffer)[offset->uval32] = value->uval32;
		break;
	default:
		return VSFVM_RET_INVALID_PARAM;
	}
	return VSFVM_RET_FINISHED;
}

static enum vsfvm_ret_t vsfvm_ext_pointer_get(struct vsfvm_thread_t *thread)
{
	struct vsfvm_var_t *result = vsfvm_get_func_argu(thread, 0);
	struct vsfvm_var_t *thiz = vsfvm_get_func_argu_ref(thread, 0);
	struct vsfvm_var_t *offset = vsfvm_get_func_argu_ref(thread, 1);
	struct vsf_buffer_t buffer;

	if (!thiz || !vsfvm_instance_of(thiz, &vsfvm_ext_pointer))
		return VSFVM_RET_INVALID_PARAM;
	buffer = thiz->inst->buffer;
	vsfvm_instance_deref(thread, thiz);
	result->type = VSFVM_VAR_TYPE_VALUE;
	switch (buffer.size)
	{
	case 1:
		result->uval32 = ((uint8_t *)buffer.buffer)[offset->uval32];
		break;
	case 2:
		result->uval32 = ((uint16_t *)buffer.buffer)[offset->uval32];
		break;
	case 4:
		result->uval32 = ((uint32_t *)buffer.buffer)[offset->uval32];
		break;
	default:
		return VSFVM_RET_INVALID_PARAM;
	}
	return VSFVM_RET_FINISHED;
}

static void vsfvm_ext_string_print(struct vsfvm_var_t *var)
{
	char *str = (char *)var->inst->buffer.buffer;
	vsfdbg_prints(str);
}
#endif

const struct vsfvm_class_t vsfvm_ext_array =
{
#ifdef VSFVM_COMPILER
	.name = "array",
#endif
#ifdef VSFVM_VM
	.type = VSFVM_CLASS_ARRAY,
	.op.print = vsfvm_ext_array_print,
#endif
};
const struct vsfvm_class_t vsfvm_ext_buffer =
{
#ifdef VSFVM_COMPILER
	.name = "buffer",
#endif
#ifdef VSFVM_VM
	.type = VSFVM_CLASS_BUFFER,
#endif
};
const struct vsfvm_class_t vsfvm_ext_pointer =
{
#ifdef VSFVM_COMPILER
	.name = "pointer",
#endif
#ifdef VSFVM_VM
	.type = VSFVM_CLASS_POINTER,
#endif
};
const struct vsfvm_class_t vsfvm_ext_string =
{
#ifdef VSFVM_COMPILER
	.name = "string",
#endif
#ifdef VSFVM_VM
	.type = VSFVM_CLASS_STRING,
	.op.print = vsfvm_ext_string_print,
#endif
};

#ifdef VSFVM_COMPILER
extern const struct vsfvm_ext_op_t vsfvm_ext_std;
static const struct vsfvmc_lexer_sym_t vsfvm_ext_stdsym[] =
{
	VSFVM_LEXERSYM_CONST("NULL", &vsfvm_ext_std, NULL, 0),
	VSFVM_LEXERSYM_CONST("true", &vsfvm_ext_std, NULL, 1),
	VSFVM_LEXERSYM_CONST("false", &vsfvm_ext_std, NULL, 0),

	VSFVM_LEXERSYM_EXTFUNC("print", &vsfvm_ext_std, NULL, NULL, -1, 0),
	VSFVM_LEXERSYM_EXTFUNC("memset", &vsfvm_ext_std, NULL, NULL, 4, 1),
	VSFVM_LEXERSYM_EXTFUNC("memcpy", &vsfvm_ext_std, NULL, NULL, 5, 2),

	VSFVM_LEXERSYM_CLASS("array", &vsfvm_ext_std, &vsfvm_ext_array),
	VSFVM_LEXERSYM_EXTFUNC("array_create", &vsfvm_ext_std, NULL, &vsfvm_ext_array, -1, 3),
	VSFVM_LEXERSYM_EXTFUNC("array_get", &vsfvm_ext_std, &vsfvm_ext_array, NULL, -1, 4),
	VSFVM_LEXERSYM_EXTFUNC("array_set", &vsfvm_ext_std, &vsfvm_ext_array, &vsfvm_ext_array, -1, 5),

	VSFVM_LEXERSYM_CLASS("buffer", &vsfvm_ext_std, &vsfvm_ext_buffer),
	VSFVM_LEXERSYM_EXTFUNC("buffer_create", &vsfvm_ext_std, NULL, &vsfvm_ext_buffer, -1, 6),
	VSFVM_LEXERSYM_EXTFUNC("buffer_get_size", &vsfvm_ext_std, &vsfvm_ext_buffer, NULL, 1, 7),

	VSFVM_LEXERSYM_CLASS("pointer", &vsfvm_ext_std, &vsfvm_ext_pointer),
	VSFVM_LEXERSYM_EXTFUNC("pointer_create", &vsfvm_ext_std, NULL, &vsfvm_ext_pointer, -1, 8),
	VSFVM_LEXERSYM_EXTFUNC("pointer_set", &vsfvm_ext_std, &vsfvm_ext_pointer, &vsfvm_ext_pointer, 3, 9),
	VSFVM_LEXERSYM_EXTFUNC("pointer_get", &vsfvm_ext_std, &vsfvm_ext_pointer, NULL, 2, 10),

	VSFVM_LEXERSYM_CLASS("string", &vsfvm_ext_std, &vsfvm_ext_string),
};
#endif

#ifdef VSFVM_VM
static const struct vsfvm_extfunc_t vsfvm_ext_stdfunc[] =
{
	VSFVM_EXTFUNC("print", vsfvm_ext_print, -1),
	VSFVM_EXTFUNC("memset", vsfvm_ext_memset, 3),
	VSFVM_EXTFUNC("memcpy", vsfvm_ext_memcpy, 3),
	// array class
	VSFVM_EXTFUNC("array_create", vsfvm_ext_array_create, -1),
	VSFVM_EXTFUNC("array_get", vsfvm_ext_array_get, -1),
	VSFVM_EXTFUNC("array_set", vsfvm_ext_array_set, -1),
	// buffer class
	VSFVM_EXTFUNC("buffer_create", vsfvm_ext_buffer_create, -1),
	VSFVM_EXTFUNC("buffer_get_size", vsfvm_ext_buffer_get_size, 1),
	// pointer class
	VSFVM_EXTFUNC("pointer_create", vsfvm_ext_pointer_create, -1),
	VSFVM_EXTFUNC("pointer_set", vsfvm_ext_pointer_set, 3),
	VSFVM_EXTFUNC("pointer_get", vsfvm_ext_pointer_get, 2),
};
#endif

static const struct vsfvm_ext_op_t vsfvm_ext_std =
{
#ifdef VSFVM_COMPILER
	.name = "std",
	.sym = vsfvm_ext_stdsym,
	.sym_num = dimof(vsfvm_ext_stdsym),
#endif
#ifdef VSFVM_VM
	.init = NULL,
	.fini = NULL,
	.func = (struct vsfvm_extfunc_t *)vsfvm_ext_stdfunc,
#endif
	.func_num = dimof(vsfvm_ext_stdfunc),
};

static struct vsfvm_ext_t std_ext;
void vsfvm_ext_register_std(void)
{
	std_ext.op = &vsfvm_ext_std;
	vsfvm_register_ext(&std_ext);
}

#endif		// VSFVM_VM || VSFVM_COMPILER
